package taller2.model.graph;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;

import taller2.interfaces.Connection;
import taller2.interfaces.Node;
import taller2.utils.Constants.Colores;
import taller2.utils.Point;

/**
 * Created by sbarrena on 30/09/14.
 */
public class NodeImpl implements Serializable,Node{
    private static final long serialVersionUID = 1L;
 	private static int idNumber = 0;
    private boolean selected;
    private boolean marked;
    private Point pos;
    private String tag;
    private String marker;
    private Colores color;
    private Integer id;
    
    private ArrayList<Connection> connections;

    // constructor
    public NodeImpl(Point pos, String nodeTag){
        this.pos = pos;
        this.setTag(nodeTag);
        this.marker = null;
        this.color = Colores.yellow;
        this.selected = false;
        this.marked = false;
        this.connections = new ArrayList<Connection>();
        id = idNumber;
        idNumber++;
    }
    
    //copy constructor
    public NodeImpl(Node thisNode) {
        this.selected = false;
        this.pos = new Point(thisNode.getPos());
        this.tag = thisNode.getTag();
        this.id = thisNode.getNumberId();
        for (Connection connection : thisNode.getConnections()) {

            this.connections.add(new ConnectionImpl((ConnectionImpl)connection));
        }
    }
    
	@Override
	public void setId(int id) {
		this.id = id;	
	}
    @Override
	public int getNumberId() {
    	return id;
	}

    // returns the connections the node has (for drawing and internal use only!!)
    public ArrayList<Connection> getConnections(){
        return connections;
    }

    // creates a connection between the two nodes (only if there is no previous one)
    public void addConnection(Node otherNode,int weight){

        if(!hasConnectionAlready(otherNode)){
            connections.add(new ConnectionImpl(weight, this, otherNode));
        }else{
        	 for(Connection connection:connections){
                 if(connection.getOtherNode() == otherNode){
                	 	connection.setWeight(weight);
                 }
        	 }
        }
        	
    }

    // returns true if the node has already a connection with the other node
    private boolean hasConnectionAlready(Node otherNode){
        for(Connection connection:connections)
            if(connection.getOtherNode() == otherNode)
                return true;
        return false;
    }

    // returns true if the nodes are equal
    public boolean equals(NodeImpl other){
    	if((other == null) || (other.getID() == null))
    		return false;
    	
        return (Float.valueOf(id) == Float.valueOf(other.getID()));
    }

    // deletes the connections from node to otherNode
    public void deleteConnections(Node otherNode){
        for (Iterator<Connection> itConn = connections.iterator(); itConn.hasNext(); ) {
            Connection connection = itConn.next();
            if (connection.getOtherNode() == otherNode) {
                itConn.remove();
            }
        }
    }

    // returns the Point
    public Point getPos() {
        return pos;
    }

    // changes the Point
    public void setPos(Point newPos) {
        pos = newPos;
    }

    @Override
    public String getID() {
        return id.toString();
    }

    // returns the tag for the node
    public String getTag() {
        return tag;
    }

    // changes the tag for the node
    public void setTag(String tag) {
        this.tag = tag;
    }

    // marks the node to be shown in other color
    public void select(){
        this.selected = true;
        changeColor(Colores.red);
    }

    // unmarks the node so its shown normally
    public void unselect(){
        this.selected = false;
        changeColor(Colores.yellow);
    }

    // returns true if the node is selected
    public boolean isSelected(){
        return this.selected;
    }

	// unselects the connections so its shown normally
	public void unselectConnections(){
		 for(Connection connection:connections){
			connection.unselect();		
		}
	}

	// returns the connection from this node to the other	
	public Connection getConnectionTo(Node other){
		for(Connection connection:getConnections()){
        	if(connection.getOtherNode().equals(other)){
        		return connection;	
        	}
		}
		return null;
	}

	// unselects the connection from this node to the other	
	public void unSelectConnectionTo(Node other){
		for(Connection connection:getConnections()){
        	if(connection.getOtherNode().equals(other)){
        		connection.unselect();	
        		break;
        	}
		}
	}
	
	// unselects the connections between this node to the other
	public void unSelectConnectionsWith(Node other){
		unSelectConnectionTo(other);
		other.unSelectConnectionTo(this);
	}	
	
	// selects the connection from this node to the other	
	public void selectConnectionTo(Node other){
		for(Connection connection:getConnections()){
        	if(connection.getOtherNode().equals(other)){
        		connection.select();	
        		break;
        	}
		}
	}
	
	// selects the connections between this node to the other
	public void selectConnectionsWith(Node other){
		selectConnectionTo(other);
		other.selectConnectionTo(this);
	}
	
	// marks the node as used
	public void mark(){
		marked = true;
	}

	// unmarks the node as used
	public void unmark(){
		marked = false;
	}
	
	// returns true if the node is marked as used
	public boolean isMarked(){
		return marked;
	}

	@Override
	// changes color of the node
	public void changeColor(Colores color) {
		this.color = color;
	}
	
	// permits having a node in two colors (half the node each)
	//only for combinig red, gree and purple. NOT YELLOW
	public void addColor(Colores colorExtra){
		
		if( this.color == Colores.green){
			if (colorExtra == Colores.red){
				this.color = Colores.greenRed;
				return;
			}
			
			if (colorExtra == Colores.purple){
				this.color = Colores.greenPurple;
				return;
			}
			
		}
		
		if( this.color == Colores.red){
			if (colorExtra == Colores.purple){
				this.color = Colores.redPurple;
				return;
			}
			
			if (colorExtra == Colores.green){
				this.color = Colores.greenRed;
				return;
			}
			
		}
		
		if( this.color == Colores.purple){
			if (colorExtra == Colores.red){
				this.color = Colores.redPurple;
				return;
			}
			
			if (colorExtra == Colores.green){
				this.color = Colores.greenPurple;
				return;
			}
			
		}
		
		//si no estaba de ningun color combinable (es decir era amarillo) -> se cambia de color
		changeColor(colorExtra);
		
	}
	
	@Override
	// returns color of the node
	public Colores getColor(){
		return this.color;
	}

    // sets the marker for dijstra's algorithm
    public void setMarker(String newMarker){
    	this.marker = newMarker;
    }
    
    // gets the marker for dijstra's algorithm
	public String getMarker(){
		return marker;
	}
   
}
